﻿namespace VividKit.ComponentModel
{
	using System;
	using System.Collections.Generic;

	public class DefaultCommandManager : ICommandManager
	{
		private readonly IDispatcher dispatcher;
		private readonly List<IWeakEventHandler> eventHandlers;
		private object dispatcherOperation;

		public DefaultCommandManager(IDispatcher dispatcher)
		{
			this.dispatcher = dispatcher;
			this.eventHandlers = new List<IWeakEventHandler>();
		}

		event EventHandler ICommandManager.RequerySuggested
		{
			add
			{
				this.eventHandlers.Add(CreateWeakEventHandler(value));
			}
			remove
			{
				Cleanup(value);
			}
		}

		protected virtual IWeakEventHandler CreateWeakEventHandler(EventHandler eventHandler)
		{
			var wehType = typeof(DefaultWeakEventHandler<>).MakeGenericType(eventHandler.Method.DeclaringType);
			var wehConstructor = wehType.GetConstructor(new[] { typeof(EventHandler) });
			var weh = (IWeakEventHandler)wehConstructor.Invoke(new[] { eventHandler });

			return weh;
		}

		void ICommandManager.InvalidateRequerySuggested()
		{
			if (this.dispatcherOperation == null)
			{
				this.dispatcherOperation = this.dispatcher.InvokeAsync(InvokeEventHandlers);
			}
		}

		private void Cleanup(EventHandler value)
		{
			for (var i = this.eventHandlers.Count - 1; i >= 0; --i)
			{
				var eventHandler = this.eventHandlers[i];
				if (eventHandler.IsAlive == false || eventHandler.Equals(value))
				{
					this.eventHandlers.RemoveAt(i);
				}
			}
		}

		private void InvokeEventHandlers()
		{
			this.dispatcherOperation = null;

			foreach (var eventHandler in this.eventHandlers)
			{
				eventHandler.Invoke(null, EventArgs.Empty);
			}

			Cleanup(null);
		}
	}
}